package org.meb.conquest.db.dao;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Order;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.metamodel.ManagedType;
import javax.persistence.metamodel.SingularAttribute;

import org.apache.commons.lang.StringUtils;
import org.meb.conquest.db.model.CardType;
import org.meb.conquest.db.model.Faction;
import org.meb.conquest.db.model.loc.Card;
import org.meb.conquest.db.model.loc.Card_;
import org.meb.conquest.db.query.CardQuery;
import org.meb.conquest.db.query.Sorting.Direction;
import org.meb.conquest.db.query.Sorting.Item;

public class CardDao extends JpaDao<Card> {

	public CardDao(EntityManager em) {
		super(em);
	}

	public List<Card> find(CardQuery query) {
		EntityManager em = getEntityManager();
		CriteriaBuilder cb = em.getCriteriaBuilder();
		CriteriaQuery<Card> cq = cb.createQuery(Card.class);
		Root<Card> root = cq.from(Card.class);
		Card example = query.getExample();

		List<Predicate> predicates = new ArrayList<Predicate>();
		if (query.isSearchDeckCards()) {
			Faction faction = query.getDeckFaction();
			Long warlordId = query.getDeckWarlordId();

			Predicate prThisFaction = cb.equal(root.get(Card_.faction), faction);
			// Predicate prThisWarlord = cb.equal(root.get(Card_.id),
			// warlordId);
			Predicate prThisSignSquad = cb.equal(root.get(Card_.warlordId), warlordId);
			Predicate prFactions = root.get(Card_.faction).in((Object[]) faction.alliance());
			Predicate prNotLoyal = cb.equal(root.get(Card_.loyal), Boolean.FALSE);
			// Predicate prNotWarlord = cb.equal(root.get(Card_.type),
			// CardType.WARLORD).not();
			Predicate prNotSignSquad = root.get(Card_.warlordId).isNull();

			predicates.add(prFactions);
			predicates.add(root.get(Card_.type).in(CardType.TOKEN, CardType.PLANET, CardType.WARLORD).not());
			// predicates.add(cb.or(prNotWarlord, prThisWarlord));
			predicates.add(cb.or(prThisSignSquad, prNotSignSquad));
			predicates.add(cb.or(prNotLoyal, prThisFaction));
		} else {
			String text = StringUtils.trimToEmpty(query.getText()).toLowerCase();
			if (StringUtils.isNotBlank(text)) {
				text = "%" + text + "%";
				List<Predicate> textPredicates = new ArrayList<Predicate>();
				if (query.isSearchInName()) {
					textPredicates.add(cb.like(cb.lower(root.get(Card_.name)), text));
				}
				if (query.isSearchInTrait()) {
					textPredicates.add(cb.like(cb.lower(root.get(Card_.trait)), text));
				}
				if (query.isSearchInText()) {
					textPredicates.add(cb.like(cb.lower(root.get(Card_.text)), text));
				}

				if (textPredicates.size() > 0) {
					predicates.add(cb.or(textPredicates.toArray(new Predicate[textPredicates.size()])));
				}
			}

			List<CardType> types = query.getTypes();
			if (types != null && types.size() > 0) {
				predicates.add(root.get(Card_.type).in(types));
			} else if (example.getType() != null) {
				predicates.add(cb.equal(root.get(Card_.type), example.getType()));
			}

			List<Long> cardSetIds = query.getCardSetIds();
			if (cardSetIds != null && cardSetIds.size() > 0) {
				predicates.add(root.get(Card_.crstId).in(cardSetIds));
			} else if (example.getCrstId() != null) {
				predicates.add(cb.equal(root.get(Card_.crstId), example.getCrstId()));
			}
		}

		Collection<Item> items = query.getSorting().items();
		if (items != null && items.size() > 0) {
			ManagedType<Card> mt = em.getMetamodel().managedType(Card.class);
			List<Order> orderList = new ArrayList<Order>();
			for (Item item : items) {
				SingularAttribute<? super Card, ?> attribute = mt.getSingularAttribute(item.getProperty());
				Path<?> path = root.get(attribute);
				if (item.getDirection() == Direction.ASC) {
					orderList.add(cb.asc(path));
				} else if (item.getDirection() == Direction.DESC) {
					orderList.add(cb.desc(path));
				}
			}
			cq.orderBy(orderList);
		}

		cq.select(root).where(predicates.toArray(new Predicate[predicates.size()]));
		return em.createQuery(cq).getResultList();
	}
}
